#ifndef FLV_H
#define FLV_H

/**
 * FLV format spec
 * http://www.adobe.com/devnet/flv/pdf/video_file_format_spec_v10.pdf
 */

/**
 * FLV glossaries
 * -----------------------------
 * hdr      FLV header
 * -        Signature
 * version  Version
 * res1     TypeFlagsReserved[5]
 * aflag    TypeFlagsAudio
 * res2     TypeFlagsReserved[1]
 * vflag    TypeFlagsVideo
 *
 * off      DataOffset
 * track    TagType
 * size     DataSize
 * time     -
 * -        Timestamp
 * -        TimestampExtended
 * stream   StreamID
 *
 * audio    AUDIODATA
 * acodec   SoundFormat
 * arate    SoundRate
 * adepth   SoundSize
 * amode    SoundType
 *
 * video    VIDEODATA
 * vframe   FrameType
 * vcodec   CodecID
 *
 * pts      PreviousTagSize
 */

#define FLV_VERSION_DEF 1
#define FLV_AFLAG_DEF 1
#define FLV_VFLAG_DEF 1

#define FLV_TRACK_AUDIO 8   //00001000
#define FLV_TRACK_VIDEO 9   //00001001
#define FLV_TRACK_SCRIPT 18 //00010010
#define FLV_TRACK_NUM 256
#define FLV_TRACK_ALL 256

#define FLV_AUDIO_MONO 0
#define FLV_AUDIO_STEREO 1
#define FLV_AMODE_NUM 2
#define FLV_AMODE_DEF FLV_AUDIO_STEREO

#define FLV_AUDIO_8BIT 0
#define FLV_AUDIO_16BIT 1
#define FLV_ADEPTH_NUM 2
#define FLV_ADEPTH_DEF FLV_AUDIO_16BIT

#define FLV_AUDIO_5512HZ 0
#define FLV_AUDIO_11025HZ 1
#define FLV_AUDIO_22050HZ 2
#define FLV_AUDIO_44100HZ 3
#define FLV_ARATE_NUM 4
#define FLV_ARATE_DEF FLV_AUDIO_44100HZ

#define FLV_AUDIO_PCM 0
#define FLV_AUDIO_ADPCM 1
#define FLV_AUDIO_MP3 2
#define FLV_AUDIO_PCMLE 3
#define FLV_AUDIO_NELLYMOSER_16KHZ_MONO 4
#define FLV_AUDIO_NELLYMOSER_8KHZ_MONO 5
#define FLV_AUDIO_NELLYMOSER 6
#define FLV_AUDIO_G711_A_LAW 7
#define FLV_AUDIO_G711_MU_LAW 8
#define FLV_AUDIO_AAC 10
#define FLV_AUDIO_SPEEX 11
#define FLV_AUDIO_MP3_8K 14
#define FLV_AUDIO_DEVICE_SPECIFIC 15
#define FLV_ACODEC_NUM 16

#define FLV_VIDEO_JPEG 1
#define FLV_VIDEO_H263 2
#define FLV_VIDEO_FSV1 3
#define FLV_VIDEO_VP6F 4
#define FLV_VIDEO_VP6A 5
#define FLV_VIDEO_FSV2 6
#define FLV_VIDEO_H264 7
#define FLV_VCODEC_NUM 16

#define FLV_VIDEO_KEY 1
#define FLV_VIDEO_INTER 2
#define FLV_VIDEO_DISPOSABLE_INTER 3
#define FLV_VIDEO_GENERATED_KEY 4
#define FLV_VIDEO_INFO 5
#define FLV_VFRAME_NUM 16
#define FLV_VFRAME_DEF FLV_VIDEO_INTER

#define FLV_HDR_LEN 9
#define FLV_TAGHDR_LEN 11
#define FLV_AUDIOHDR_LEN 1
#define FLV_VIDEOHDR_LEN 1
#define FLV_PTS_LEN 4

#include <stddef.h>

/**
 * Note on flv structure naming
 *
 * hdr pts0 taghdr [(audio|video)hdr] tagdata pts ....
 *                 |<--      payload     -->|
 */
struct tagbuf {
	size_t hdrlen;
	size_t len; /* length of payload */
	int is_tag; /* 0: hdr; 1: undetermined; 2: tag */
	int is_linear;

	char *data;
	size_t allocated;
	size_t used;
	size_t read;
};

static inline char *tagbuf_hdr(const struct tagbuf *b)
{
	return b->data;
}
static inline char *tagbuf_payload(const struct tagbuf *b)
{
	return &b->data[b->hdrlen];
}
static inline char *tagbuf_pts(const struct tagbuf *b)
{
	return &b->data[b->hdrlen + b->len];
}

extern const char *const flv_track_str[256];
extern const char *const flv_acodec_str[16];
extern const char *const flv_arate_str[4];
extern const char *const flv_adepth_str[2];
extern const char *const flv_amode_str[2];
extern const char *const flv_vframe_str[16];
extern const char *const flv_vcodec_str[16];

#define GET(field, type, base, expr) \
static inline type flv_##field(const struct tagbuf *b)\
{\
	unsigned char *s = (unsigned char *)base;\
	return expr;\
}
#define SET(field, type, base, statement) \
static inline void flv_set_##field(struct tagbuf *b, type x)\
{\
	unsigned char *s = (unsigned char *)base;\
	statement;\
}
static inline int flv_is_tag(const struct tagbuf *b)
{
	if (b->is_tag != 1)
		return b->is_tag;
	return 2 * (tagbuf_hdr(b)[0] != 'F' || tagbuf_hdr(b)[1] != 'L' ||
				tagbuf_hdr(b)[2] != 'V');
}

/* if !flv_is_tag() */
GET(version, unsigned char, tagbuf_hdr(b), s[3])
SET(version, unsigned char, tagbuf_hdr(b), s[3] = x)
GET(res1,    unsigned char, tagbuf_hdr(b), s[4] >> 3 & 31)
SET(res1,    unsigned char, tagbuf_hdr(b), s[4] = (x & 31) << 3 | (s[4] & 7))
GET(aflag,   unsigned char, tagbuf_hdr(b), s[4] >> 2 & 1)
SET(aflag,   unsigned char, tagbuf_hdr(b), s[4] = (x & 1) << 2 | (s[4] & 251))
GET(res2,    unsigned char, tagbuf_hdr(b), s[4] >> 1 & 1)
SET(res2,    unsigned char, tagbuf_hdr(b), s[4] = (x & 1) << 1 | (s[4] & 253))
GET(vflag,   unsigned char, tagbuf_hdr(b), s[4] & 1)
SET(vflag,   unsigned char, tagbuf_hdr(b), s[4] = (x & 1) | (s[4] & 254))
GET(off,     unsigned long, tagbuf_hdr(b), s[8] | s[7] << 8 | s[6] << 16 | s[5] << 24)
SET(off,     unsigned long, tagbuf_hdr(b), s[5] = x >> 24;
						s[6] = x >> 16;
						s[7] = x >> 8;
						s[8] = x)

/* if is_tag == 2 */
GET(track,   unsigned char, tagbuf_hdr(b), s[0])
SET(track,   unsigned char, tagbuf_hdr(b), s[0] = x)
GET(size,    unsigned long, tagbuf_hdr(b), s[3] | s[2] << 8 | s[1] << 16)
SET(size,    unsigned long, tagbuf_hdr(b), s[1] = x >> 16;
						s[2] = x >> 8;
						s[3] = x)
GET(time,    long,          tagbuf_hdr(b), s[6] | s[5] << 8 | s[4] << 16 | s[7] << 24)
SET(time,    unsigned long, tagbuf_hdr(b), s[4] = x >> 16;
						s[5] = x >> 8;
						s[6] = x;
						s[7] = x >> 24)
GET(stream,  unsigned long, tagbuf_hdr(b), s[10] | s[9] << 8 | s[8] << 16)
SET(stream,  unsigned long, tagbuf_hdr(b), s[8] = x >> 16;
						s[9] = x >> 8;
						s[10] = x)

/* if track == audio */
GET(acodec,  unsigned char, tagbuf_payload(b), s[0] >> 4 & 15)
SET(acodec,  unsigned char, tagbuf_payload(b), s[0] = (x & 15) << 4 | (s[0] & 15))
GET(arate,   unsigned char, tagbuf_payload(b), s[0] >> 2 & 3)
SET(arate,   unsigned char, tagbuf_payload(b), s[0] = (x & 3) << 2 | (s[0] & 243))
GET(adepth,  unsigned char, tagbuf_payload(b), s[0] >> 1 & 1)
SET(adepth,  unsigned char, tagbuf_payload(b), s[0] = (x & 1) << 1 | (s[0] & 253))
GET(amode,   unsigned char, tagbuf_payload(b), s[0] & 1)
SET(amode,   unsigned char, tagbuf_payload(b), s[0] = (x & 1) | (s[0] & 254))

/* if track == video */
GET(vframe,  unsigned char, tagbuf_payload(b), s[0] >> 4 & 15)
SET(vframe,  unsigned char, tagbuf_payload(b), s[0] = (x & 15) << 4 | (s[0] & 15))
GET(vcodec,  unsigned char, tagbuf_payload(b), s[0] & 15)
SET(vcodec,  unsigned char, tagbuf_payload(b), s[0] = (x & 15) | (s[0] & 240))

GET(pts,     unsigned long, tagbuf_pts(b), s[3] | s[2] << 8 | s[1] << 16 | s[0] << 24)
SET(pts,     unsigned long, tagbuf_pts(b), s[0] = x >> 24;
						s[1] = x >> 16;
						s[2] = x >> 8;
						s[3] = x)

const char *flvname(const char *const *names, unsigned index);
struct tagbuf *tagbuf_alloc(void);
struct tagbuf *tagbuf_realloc(struct tagbuf *b, size_t size);
void tagbuf_free(struct tagbuf *);
void tagbuf_clear(struct tagbuf *);
#endif /* FLV_H */
